package PCB;

import javax.swing.*;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import javax.imageio.ImageIO;

import static java.lang.Double.parseDouble;
import static java.lang.Thread.sleep;
import static javax.swing.WindowConstants.DISPOSE_ON_CLOSE;

public class MainFrame extends JFrame {
    private JButton bFIle, bStart, bStop;//三个按钮
    private JTextField tTime, tCur,time;
    private JTextArea tAll, tReady, tInput, tOutput, tWait;//队列文本域显示
    private File file;
    private JLabel lInfo,now;
    //就绪队列
    private ArrayList<PCB> allQue = new ArrayList<PCB>();
    //备选就绪队列
    private ArrayList<PCB> readyQue = new ArrayList<PCB>();
    //输入等待队列
    private ArrayList<PCB> inQue = new ArrayList<PCB>();
    //输出等待队列
    private ArrayList<PCB> outQue = new ArrayList<PCB>();
    //等待队列
    private ArrayList<PCB> waitQue = new ArrayList<PCB>();

    /*
     * Java内存模型规定了所有的变量都存储在主内存中。每条线程中还有自己的工作内存，线程的工作内存中保存了被该线程所使用到的变量
     * （这些变量是从主内存中拷贝而来）。线程对变量的所有操作（读取，赋值）都必须在工作内存中进行。不同线程之间也无法直接访问对方工作内存中的变量，
     * 线程间变量值的传递均需要通过主内存来完成。
     * */
    private volatile Thread blinker;

    //表示当前时间片
    private long count = 0;

    // 自定义背景面板类
    class BackgroundPanel extends JPanel {
        private Image backgroundImage;

        public BackgroundPanel(Image image) {
            this.backgroundImage = image;
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            if (backgroundImage != null) {
                // 绘制背景图片，自适应面板大小
                g.drawImage(backgroundImage, 0, 0, getWidth(), getHeight(), this);
            }
        }
    }

    public MainFrame() {
        //初始化界面
        init();
        //选择文件 实现接口类对象
        bFIle.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                chooseFile();
                readFile();
                saveLog("读取文件成功!\r\n________________________\r\n");
                showAll(allQue);
            }
        });

        //开始按钮监听事件
        bStart.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (allQue.size()==0) {
                    JOptionPane.showMessageDialog(null, "请重新选择文件!", "提示", JOptionPane.INFORMATION_MESSAGE);
                    return;
                }
                if (boolTTime()) {
                    initQue();
                    startRun();
                }
            }
        });

        //停止按钮监听事件
        bStop.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                bStart.setText("继续调度");
                blinker = null;
            }
        });
    }

    //保存日志
    // 类中添加这两个变量
    private static boolean isInitialized = false;
    private static String dynamicLogFileName = null;

    // 保存日志
    private void saveLog(String str) {
        // 创建log目录
        File logDir = new File("log");
        if (!logDir.exists()) {
            logDir.mkdirs(); // 使用 mkdirs() 确保多级目录也能创建
        }

        // 如果是第一次写入，则生成带时间戳的文件名
        if (!isInitialized) {
            String timestamp = new java.text.SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new java.util.Date());
            dynamicLogFileName = "log-" + timestamp + ".txt";
            isInitialized = true;
        }

        // 使用动态生成的文件名
        File file = new File(logDir, dynamicLogFileName);

        try {
            // 如果文件不存在，则创建
            if (!file.exists()) {
                file.createNewFile();
            }

            // 使用追加模式写入日志
            FileOutputStream out = new FileOutputStream(file, true); // true 表示追加模式

            // 添加换行和时间戳
            String time = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new java.util.Date());
            String logLine = "\r\n[" + time + "] " + str;
//            String logLine = str + "\r\n";

            // 写入内容
            out.write(logLine.getBytes(StandardCharsets.UTF_8));
            out.flush();
            out.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //检查时间片是否合法
    private boolean boolTTime() {
        //判断时间片
        if (tTime.getText().equals("")) {
            //时间片大小
            JOptionPane.showMessageDialog(null, "请输入时间片大小(数字)!", "提示", JOptionPane.INFORMATION_MESSAGE);
            return false;
        } else {
            try {
                //是否为数值
                parseDouble(tTime.getText());
            } catch (Exception ex) {
                JOptionPane.showMessageDialog(null, "时间片大小为数字!请重新输入!", "提示", JOptionPane.INFORMATION_MESSAGE);
                return false;
            }
        }
        return true;
    }


    //开始调度
    public void startRun() {
        Runnable runnable = new Runnable() {
            public void run() {
                saveLog("------正在进行调度-------");
                lInfo.setText("------正在进行调度-------");
                while (allQue.size() > 0 && blinker != null) {
                    try {
                        count++; // 用于计数
                        System.out.println(count);

                        saveLog("\r\n######################## 当前时间片:"+count+" ########################");
                        
                        // 先执行就绪队列
                        runReady();
                        // 添加当前运行进程的日志 记录当前进程
                        if (!tCur.getText().isEmpty()) {

                            saveLog("当前时间片执行程序：" + tCur.getText());
                        }
                        
                        // 显示所有队列的当前状态
                        showAll(allQue);
//                        saveLog("就绪队列:\t" + tAll.getText() + "\r\n");
                        showReady(readyQue);
                        saveLog("后备就绪队列:\t" + tReady.getText() + "\r\n");
                        showIn(inQue);
                        saveLog("输入等待队列:\t" + tInput.getText() + "\r\n");
                        showOut(outQue);
                        saveLog("输出等待队列:\t" + tOutput.getText() + "\r\n");
                        showWait(waitQue);
                        saveLog("其他等待队列:\t" + tWait.getText() + "\r\n");

                        saveLog("---------------------------------------------\n\n");

                        // 等待一个时间片
                        sleep(Long.parseLong(tTime.getText()));
                        
                        // 在下一个时间片开始时执行其他队列
                        if (blinker != null) {
                            runIn();
                            runOut();
                            runWait();
                        }

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                if (allQue.size() == 0) {
                    bStart.setText("开始调度");
                    tCur.setText("");
                    lInfo.setText("调度已完成");
                    saveLog("调度已完成");
                } else {
                    lInfo.setText("调度已停止");
                    saveLog("调度已停止");
                }
            }
        };
        blinker = new Thread(runnable);
        blinker.start();
    }

    private void showAll(ArrayList<PCB> allQue) {
        tAll.setText(""); // 清空显示区域
        for (PCB p : allQue) {
            // 显示进程名称和当前指令的剩余执行时间
            tAll.setText(tAll.getText() + "\r\n" + p.getpName()+"("+p.getpInstructions().get(p.getCurrentInstruction()).getIRemainTime()+")");
        }
    }

    private void showWait(ArrayList<PCB> waitQue) {
        tWait.setText(""); // 清空显示区域
        for (PCB p : waitQue) {
            // 显示等待队列中每个进程的名称和当前等待指令的剩余时间
            tWait.setText(tWait.getText() + "\r\n" + p.getpName()+"("+p.getpInstructions().get(p.getCurrentInstruction()).getIRemainTime()+")");
        }
    }

    private void showOut(ArrayList<PCB> outQue) {
        tOutput.setText("");
        for (PCB p : outQue) {
            // 显示输出队列中每个进程的名称和当前输出指令的剩余时间
            tOutput.setText(tOutput.getText() + "\r\n" + p.getpName()+"("+p.getpInstructions().get(p.getCurrentInstruction()).getIRemainTime()+")");
        }
    }

    private void showIn(ArrayList<PCB> inQue) {
        tInput.setText("");
        for (PCB p : inQue) {
            // 显示输入队列中每个进程的名称和当前输出指令的剩余时间
            tInput.setText(tInput.getText() + "\r\n" + p.getpName()+"("+p.getpInstructions().get(p.getCurrentInstruction()).getIRemainTime()+")");
        }
    }

    private void showReady(ArrayList<PCB> que) {
        tReady.setText("");
        for (PCB p : que) {
            // 显示就绪队列中每个进程的名称和当前CPU指令的剩余时间
            tReady.setText(tReady.getText() + "\r\n" + p.getpName()+"("+p.getpInstructions().get(p.getCurrentInstruction()).getIRemainTime()+")");
        }
    }

    // 初始化队列
    private void initQue() {
        //把就绪进程排成一个就绪队列,即readyQue
        //I，O，W三条指令实际上是不占有CPU的，执行这三条指令就应该将进程放入对应的等待队列（输入等待队列，输出等待队列 ，其他等待队列）。
        //先清空队列
        readyQue.clear();
        inQue.clear();
        outQue.clear();
        waitQue.clear();
        //根据每个进程的第一条指令类型分配到相应队列
        for (PCB p : allQue
        ) {
            if (p.getpInstructions().get(0).getIName() == 'C') {
                readyQue.add(p);
            } else if (p.getpInstructions().get(0).getIName() == 'I') {
                inQue.add(p);
            } else if (p.getpInstructions().get(0).getIName() == 'O') {
                outQue.add(p);
            } else if (p.getpInstructions().get(0).getIName() == 'W') {
                waitQue.add(p);
            }
        }
    }

    // 处理就绪队列
    private void runReady() {
        if (readyQue.size() > 0) {
            PCB currentProcess = readyQue.get(0); // 获取队首进程
            Instructions currentInstruction = currentProcess.getpInstructions().get(0);
            
            // 执行当前指令，减少剩余时间
            currentInstruction.subIRemainTime();
            // 只有当进程在就绪队列中（执行CPU操作）时才显示当前进程
            tCur.setText(currentProcess.getpName());
            time.setText(Long.toString(count));

            if (currentInstruction.getIRemainTime() == 0) {
                // 如果当前指令执行完成，移除它
                currentProcess.getpInstructions().remove(0);
                
                // 检查是否还有下一条指令
                if (currentProcess.getpInstructions().size() > 0) {
                    // 获取下一条指令并重置其剩余时间
                    Instructions nextInstruction = currentProcess.getpInstructions().get(0);
                    nextInstruction.setIRemainTime(nextInstruction.getIRuntime());
                    
                    // 根据下一条指令的类型将进程添加到相应的队列
                    char nextType = nextInstruction.getIName();
                    if (nextType == 'C') {
                        readyQue.add(currentProcess);
                    } else if (nextType == 'I') {
                        inQue.add(currentProcess);
                    } else if (nextType == 'O') {
                        outQue.add(currentProcess);
                    } else if (nextType == 'W') {
                        waitQue.add(currentProcess);
                    } else if (nextType == 'H') {
                        allQue.remove(currentProcess);
                    }
                }
                readyQue.remove(0);
            } else {
                // 如果当前指令未完成，将进程移到就绪队列末尾
                readyQue.add(currentProcess);
                readyQue.remove(0);
            }
        } else {
            // 如果没有进程在就绪队列中，显示NONE
            tCur.setText("NONE");
        }
    }

    private void runIn() {
        for (int i = 0; i < inQue.size(); i++) {
            if (inQue.size() > 0) {
                PCB currentProcess = inQue.get(i);
                Instructions currentInstruction = currentProcess.getpInstructions().get(0);
                
                // 执行当前指令
                currentInstruction.subIRemainTime();
                
                if (currentInstruction.getIRemainTime() == 0) {
                    // 如果当前指令执行完成，移除它
                    currentProcess.getpInstructions().remove(0);
                    
                    // 检查是否还有下一条指令
                    if (currentProcess.getpInstructions().size() > 0) {
                        // 获取下一条指令并重置其剩余时间
                        Instructions nextInstruction = currentProcess.getpInstructions().get(0);
                        nextInstruction.setIRemainTime(nextInstruction.getIRuntime());
                        
                        // 根据下一条指令的类型将进程添加到相应的队列
                        char nextType = nextInstruction.getIName();
                        if (nextType == 'C') {
                            readyQue.add(currentProcess);
                        } else if (nextType == 'I') {
                            inQue.add(currentProcess);
                        } else if (nextType == 'O') {
                            outQue.add(currentProcess);
                        } else if (nextType == 'W') {
                            waitQue.add(currentProcess);
                        } else if (nextType == 'H') {
                            allQue.remove(currentProcess);
                        }
                    }
                    inQue.remove(i);
                }
            }
        }
    }

    private void runOut() {
        for (int i = 0; i < outQue.size(); i++) {
            if (outQue.size() > 0) {
                PCB currentProcess = outQue.get(i);
                Instructions currentInstruction = currentProcess.getpInstructions().get(0);
                
                // 执行当前指令
                currentInstruction.subIRemainTime();
                
                if (currentInstruction.getIRemainTime() == 0) {
                    // 如果当前指令执行完成，移除它
                    currentProcess.getpInstructions().remove(0);
                    
                    // 检查是否还有下一条指令
                    if (currentProcess.getpInstructions().size() > 0) {
                        // 获取下一条指令并重置其剩余时间
                        Instructions nextInstruction = currentProcess.getpInstructions().get(0);
                        nextInstruction.setIRemainTime(nextInstruction.getIRuntime());
                        
                        // 根据下一条指令的类型将进程添加到相应的队列
                        char nextType = nextInstruction.getIName();
                        if (nextType == 'C') {
                            readyQue.add(currentProcess);
                        } else if (nextType == 'I') {
                            inQue.add(currentProcess);
                        } else if (nextType == 'O') {
                            outQue.add(currentProcess);
                        } else if (nextType == 'W') {
                            waitQue.add(currentProcess);
                        } else if (nextType == 'H') {
                            allQue.remove(currentProcess);
                        }
                    }
                    outQue.remove(i);
                }
            }
        }
    }

    private void runWait() {
        if (waitQue.size() > 0) {
            PCB currentProcess = waitQue.get(0);
            Instructions currentInstruction = currentProcess.getpInstructions().get(0);
            
            // 执行当前指令
            currentInstruction.subIRemainTime();
            
            if (currentInstruction.getIRemainTime() == 0) {
                // 如果当前指令执行完成，移除它
                currentProcess.getpInstructions().remove(0);
                
                // 检查是否还有下一条指令
                if (currentProcess.getpInstructions().size() > 0) {
                    // 获取下一条指令并重置其剩余时间
                    Instructions nextInstruction = currentProcess.getpInstructions().get(0);
                    nextInstruction.setIRemainTime(nextInstruction.getIRuntime());
                    
                    // 根据下一条指令的类型将进程添加到相应的队列
                    char nextType = nextInstruction.getIName();
                    if (nextType == 'C') {
                        readyQue.add(currentProcess);
                    } else if (nextType == 'I') {
                        inQue.add(currentProcess);
                    } else if (nextType == 'O') {
                        outQue.add(currentProcess);
                    } else if (nextType == 'W') {
                        waitQue.add(currentProcess);
                    } else if (nextType == 'H') {
                        allQue.remove(currentProcess);
                    }
                }
                waitQue.remove(0);
            } else {
                // 如果当前指令未完成，将进程移到等待队列末尾
                waitQue.add(currentProcess);
                waitQue.remove(0);
            }
        }
    }

    private void readFile() {
        if (file != null) {
            try {
                // 创建文件读取器
                BufferedReader in = new BufferedReader(new FileReader(file));
                String str;
                allQue.clear();  // 清空现有队列
                PCB pcb = null;

                // 逐行读取文件
                while ((str = in.readLine()) != null) {
                    if (str.charAt(0) == 'P') {
                        // 如果是新进程（以P开头）
                        pcb = new PCB();
                        pcb.setpName(str);
                    } else {
                        // 如果是指令
                        Instructions instructions = new Instructions();
                        instructions.setIName(str.charAt(0));  // 设置指令类型
                        instructions.setIRuntime(parseDouble(str.substring(1)));  // 设置运行时间
                        instructions.setIRemainTime(instructions.getIRuntime());  // 设置剩余时间

                        pcb.getpInstructions().add(instructions);  // 添加指令到进程

                        if (instructions.getIName() == 'H') {
                            // 如果是结束指令，将进程添加到队列
                            allQue.add(pcb);
                        }
                    }
                }
            } catch (IOException e) {
                System.out.println("文件读取错误!");
            }
        }
    }

    private void chooseFile() {
        // 创建文件选择器
        FileNameExtensionFilter filter = new FileNameExtensionFilter("*.txt", "txt");
        JFileChooser jfc = new JFileChooser(".");  // 从当前目录开始
        jfc.setFileFilter(filter);  // 只显示txt文件
        jfc.setMultiSelectionEnabled(false);  // 不允许多选
        jfc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);

        // 显示文件选择对话框
        int result = jfc.showSaveDialog(null);

        // 如果用户选择了文件
        if (result == JFileChooser.APPROVE_OPTION) {
            file = jfc.getSelectedFile();  // 保存选中的文件
        }
    }

    private void init() {
        // 设置主窗口属性
        setTitle("时间片轮转调度");
        setBounds(200, 200, 1000, 600);
        setDefaultCloseOperation(DISPOSE_ON_CLOSE);

        // 创建背景面板并设置为内容面板
        try {
            // 加载背景图片
            Image backgroundImage = ImageIO.read(new File("./img/read.jpg"));
            BackgroundPanel backgroundPanel = new BackgroundPanel(backgroundImage);
            setContentPane(backgroundPanel);
            backgroundPanel.setLayout(null);
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 初始化按钮
        bFIle = new JButton("打开文件");
        bFIle.setBounds(50, 50, 150, 30);
        bFIle.setOpaque(false);
        bFIle.setContentAreaFilled(false);
        bFIle.setBorderPainted(true);
        add(bFIle);

        bStart = new JButton("开始调度");
        bStart.setBounds(220, 50, 150, 30);
        bStart.setOpaque(false);
        bStart.setContentAreaFilled(false);
        bStart.setBorderPainted(true);
        add(bStart);

        bStop = new JButton("暂停调度");
        bStop.setBounds(390, 50, 150, 30);
        bStop.setOpaque(false);
        bStop.setContentAreaFilled(false);
        bStop.setBorderPainted(true);
        add(bStop);

        // 初始化其他组件
        JLabel ltime = new JLabel("时间片大小:");
        ltime.setBounds(560, 50, 80, 30);
        add(ltime);

        tTime = new JTextField();
        tTime.setBounds(650, 50, 150, 30);
        tTime.setText("500");
        add(tTime);

        JLabel lCur = new JLabel("当前运行进程:");
        now = new JLabel("当前时间片:");
        now.setBounds(250, 100, 150, 30);
        lCur.setBounds(50, 100, 150, 30);
        add(lCur);
        add(now);

        tCur = new JTextField();
        tCur.setBounds(50, 130, 150, 30);
        add(tCur);

        time = new JTextField();
        time.setBounds(250, 130, 150, 30);
        add(time);

        lInfo = new JLabel("");
        lInfo.setBounds(550, 130, 300, 30);
        add(lInfo);

        // 初始化文本区域
        JLabel lall = new JLabel("就绪队列:");
        lall.setBounds(50, 200, 150, 30);
        add(lall);

        tAll = new JTextArea(6, 4);
        tAll.setBounds(50, 230, 150, 250);
        tAll.setOpaque(false);
        tAll.setBackground(new Color(255, 255, 255, 150));
        tAll.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK, 4),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)
        ));
        tAll.setFont(new Font("微软雅黑", Font.BOLD, 18)); // 设置字体
        tAll.setForeground(new Color(0, 255, 0)); // 设置文字颜色
        add(tAll);

        JLabel lr = new JLabel("后备就绪队列:");
        lr.setBounds(220, 200, 150, 30);
        add(lr);

        tReady = new JTextArea(6, 4);
        tReady.setBounds(220, 230, 150, 250);
        tReady.setOpaque(false);
        tReady.setBackground(new Color(255, 255, 255, 150));
        tReady.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK, 4),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)
        ));
        tReady.setFont(new Font("微软雅黑", Font.BOLD, 18));
        tReady.setForeground(new Color(0, 255, 0));
        add(tReady);

        JLabel lin = new JLabel("输入等待队列:");
        lin.setBounds(390, 200, 150, 30);
        add(lin);

        tInput = new JTextArea(6, 4);
        tInput.setBounds(390, 230, 150, 250);
        tInput.setOpaque(false);
        tInput.setBackground(new Color(255, 255, 255, 150));
        tInput.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK, 4),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)
        ));
        tInput.setFont(new Font("微软雅黑", Font.BOLD, 18));
        tInput.setForeground(new Color(0, 255, 0));
        add(tInput);

        JLabel lout = new JLabel("输出等待队列:");
        lout.setBounds(560, 200, 150, 30);
        add(lout);

        tOutput = new JTextArea(6, 4);
        tOutput.setBounds(560, 230, 150, 250);
        tOutput.setOpaque(false);
        tOutput.setBackground(new Color(255, 255, 255, 150));
        tOutput.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK, 4),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)
        ));
        tOutput.setFont(new Font("微软雅黑", Font.BOLD, 18));
        tOutput.setForeground(new Color(0, 255, 0));
        add(tOutput);

        JLabel lw = new JLabel("其他等待队列:");
        lw.setBounds(730, 200, 150, 30);
        add(lw);

        tWait = new JTextArea(6, 4);
        tWait.setBounds(730, 230, 150, 250);
        tWait.setOpaque(false);
        tWait.setBackground(new Color(255, 255, 255, 150));
        tWait.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLineBorder(Color.BLACK, 4),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)
        ));
        tWait.setFont(new Font("微软雅黑", Font.BOLD, 18));
        tWait.setForeground(new Color(0, 255, 0));
        add(tWait);

        // 设置窗口可见
        setVisible(true);
    }

    public static void main(String[] args) {
        new MainFrame();
    }

}
